//【运算符】
/**
 * JavaScript 拥有如下类型的运算符。本节描述了运算符和运算符的优先级
 * 赋值运算符（Assignment operators）
 * 比较运算符（Comparison operators）
 * 算数运算符（Arithmetic operators）
 * 位运算符（Bitwise operators）
 * 逻辑运算符（Logical operators）
 * 字符串运算符（String operators）
 * 条件（三元）运算符（Conditional operator）
 * 逗号运算符（Comma operator）
 * 一元运算符（Unary operators）
 * 关系运算符（Relational operator）
 *
 * JavaScript 拥有二元和一元运算符和一个特殊的三元运算符（条件运算符）
 * 一个二元运算符需要两个操作数，分别在运算符的前面和后面：
 *
 * 操作数 1 运算符 操作数 2
 * 例如：
 * 3+4 或 x*y
 * 一个一元运算符需要一个操作数，在运算符前面或后面：
 *
 * 运算符 操作数
 * 或
 * 操作数 运算符
 * 例如，x++ 或 ++x
 */

/**
 * 赋值运算符
 * 赋值运算符将它右边操作数的值赋给它左边的操作数
 * 最简单的赋值运算符是等于（=），它将右边的操作数值赋给左边的操作数
 * 那么 x = y 就是将 y 的值赋给 x
 *
 * 还有一些复合赋值操作符，它们是下表列出的这些操作的简写：
 * 名字：           简写的运算符：      含义：
 * 赋值	             x = y	        x = y
 * 加法赋值           x += y	      x = x + y
 * 减法赋值           x -= y	      x = x - y
 * 乘法赋值           x *= y	      x = x * y
 * 除法赋值           x /= y	      x = x / y
 * 求余赋值           x %= y	      x = x % y
 * 求幂赋值           x **= y	    x = x ** y
 * 左移位赋值          x <<= y       x = x << y
 * 右移位赋值          x >>= y	    x = x >> y
 * 无符号右移位赋值     x >>>= y	   x = x >>> y
 * 按位与赋值          x &= y	       x = x & y
 * 按位异或赋值        x ^= y         x = x ^ y
 * 按位或赋值          x |= y	       x = x | y
 * 逻辑与赋值          x &&= f()     x && (x = f())
 * 逻辑或赋值          x ||= f()     x || (x = f())
 * 逻辑空赋值          x ??= f()     x ?? (x = f())
 */

/**
 * 解构
 * 对于更复杂的赋值，解构赋值语法是一个能从数组或对象对应的数组结构或对象字面量里提取数据的 Javascript 表达式
 * var foo = ["one", "two", "three"];
 *
 * // 不使用解构
 * var one = foo[0];
 * var two = foo[1];
 * var three = foo[2];
 *
 * // 使用解构
 * var [one, two, three] = foo;
 */

/**
 * 比较运算符
 * 比较运算符比较它的操作数并返回一个基于表达式是否为真的逻辑值
 * 操作数可以是数字，字符串，逻辑，对象值
 * 字符串比较是基于标准的字典顺序，使用 Unicode 值
 * 在多数情况下，如果两个操作数不是相同的类型，JavaScript 会尝试转换它们为恰当的类型来比较
 * 这种行为通常发生在数字作为操作数的比较
 * 类型转换的例外是使用 === 和 !== 操作符，它们会执行严格的相等和不相等比较
 * 这些运算符不会在检查相等之前转换操作数的类型
 *
 * 运算符	                                  描述                         	       返回 true 的示例
 * 等于 Equal (==)	                    如果两边操作数相等时返回 true	           3 == var1 "3" == var1 3 == '3'
 * 不等于 Not equal (!=)	                如果两边操作数不相等时返回 true	           var1 != 4 var2 != "3"
 * 全等 Strict equal (===)	            两边操作数相等且类型相同时返回 true          3 === var1
 * 不全等 Strict not equal (!==)	        两边操作数不相等或类型不同时返回 true	       var1 !== "3" 3 !== '3'
 * 大于 Greater than (>)	                左边的操作数大于右边的操作数返回 true	        var2 > var1 "12" > 2
 * 大于等于 Greater than or equal (>=)	左边的操作数大于或等于右边的操作数返回 true	var2 >= var1 var1 >= 3
 * 小于 Less than (<)	                左边的操作数小于右边的操作数返回 true	        var1 < var2 "2" < 12
 * 小于等于 Less than or equal (<=)	    左边的操作数小于或等于右边的操作数返回 true	var1 <= var2 var2 <= 5
 */

/**
 * 算数运算符
 * 算术运算符使用数值 (字面量或者变量) 作为操作数并返回一个数值
 * 标准的算术运算符就是加减乘除 (+ - * /)
 * 当操作数是浮点数时，这些运算符表现得跟它们在大多数编程语言中一样（特殊要注意的是，除零会产生Infinity）
 * 示例：
 * 1 / 2; // 0.5
 * 1 / 2 == 1.0 / 2.0; // true
 * 除了标准的算术运算符（+， - ，* /），JavaScript 还提供了下表中的算术运算符
 * 运算符:                    示例：
 * 求余（%）                12 % 5 返回 2
 * 自增（++）               var x=3; console.log(++x); //4
 *                         console.log(x); //4
 *                         var y=3; console.log(y++); //3
 *                         console.log(y); //4
 *
 * 自减（--）               var x=3; console.log(--x); //输入 2,x=2var
 *                          y=3;console.log(y--);//输出 3,x=2;
 *
 * 一元负值符（-）             var x=3; console.log(-x); //输入 -3
 *
 * 一元正值符（+）             console.log( +'3' ); // 3
 *                           console.log( '3' ); // '3'
 *                           console.log(+true); // 1
 *
 * 指数运算符（**）            2 ** 3 返回 8
 *                          10 ** -1 返回 0.1
 */

/**
 * 位运算符
 * 位运算符将它的操作数视为 32 位元的二进制串（0 和 1 组成）而非十进制八进制或十六进制数
 * 例如：十进制数字 9 用二进制表示为 1001，位运算符就是在这个二进制表示上执行运算
 * 但是返回结果是标准的 JavaScript 数值
 *
 * 下表总结了 JavaScript 的位运算符:
 * 算子	                    用法	                                描述
 * 按位与 AND	            a & b	                在 a,b 的位表示中，每一个对应的位都为 1 则返回 1，否则返回 0.
 * 按位或 OR	a |             b	                    在 a,b 的位表示中，每一个对应的位，只要有一个为 1 则返回 1，否则返回 0.
 * 按位异或 XOR	            a ^ b	                在 a,b 的位表示中，每一个对应的位，两个不相同则返回 1，相同则返回 0.
 * 按位非 NOT	            ~ a	                    反转被操作数的位。
 * 左移 shift	            a << b	                将 a 的二进制串向左移动 b 位，右边移入 0.
 * 算术右移	                a >> b	                把 a 的二进制表示向右移动 b 位，丢弃被移出的所有位。(译注：算术右移左边空出的位是根据最高位是 0 和 1 来进行填充的)
 * 无符号右移(左边空出位用 0 填充)	a >>> b	             把 a 的二进制表示向右移动 b 位，丢弃被移出的所有位，并把左边空出的位都填充为 0
 */

/**
 * 位逻辑运算符
 * 概念上来讲，位逻辑运算符工作流程如下：
 *
 * 操作数被转换为 32bit 整數，以位序列（0 和 1 组成）表示。若超過 32bits，則取低位 32bit，如下所示：
 *   Before: 11100110111110100000000000000110000000000001
 *   After:              10100000000000000110000000000001
 * 第一个操作数的每一位都与第二个操作数的对应位组对：第一位对应第一位，第二位对应第二位，以此类推。
 * 运算符被应用到每一对"位"上，最终的运算结果由每一对“位”的运算结果组合起来。
 * 例如，十进制数 9 的二进制表示是 1001，十进制数 15 的二进制表示是 1111.
 * 因此当位运算符应用到这两个值时，结果如下：
 *
 * 表达式	结果	二进制描述
 * 15 & 9	9	1111 & 1001 = 1001
 * 15 | 9	15	1111 | 1001 = 1111
 * 15 ^ 9	6	1111 ^ 1001 = 0110
 * ~15	-16	~ 0000 0000 … 0000 1111 = 1111 1111 … 1111 0000
 * ~9	-10	~ 0000 0000 … 0000 1001 = 1111 1111 … 1111 0110
 */

/**
 * 移位运算符
 * 移位运算符带两个操作数：第一个是待移位的数，第二个是指定第一个数要被移多少位的数
 * 移位的方向由运算符来控制
 *
 * 移位运算符把操作数转为 32bit 整数，然后得出一个与待移位数相同种类的值
 *
 * 移位运算符列表如下
 *
 * 运算符:	            描述:	                                                示例:
 * <<（左移位）	       将第一个操作数向左移动指定数量的位         9<<2 产生 36，因为 1001 移位 2 比特向左变为 100100，它是 36
 *                     左边移出位被抛弃
 *                     左边移出的几位被丢弃
 *                     右边多出的空位由 0 补齐
 *
 * >>（带符号右移）	   将第一个操作数向右移动指定数量的位         9<<2产生 2，因为 1001 移位 2 位向右变为 10，其是 2。同样，-9>>2 产生 -3，由于符号被保留
 *                     右边移出位被抛弃
 *                     左边多出的空位由原值的最左边数字补齐
 * >>>（补零右移）	   将第一个操作数向右移动指定数量的位          19>>>2产生 4，因为 10011 移位 2 位向右变为 100，它是 4。
 *                     右边移出位被抛弃。左边多出的空位由 0 补齐    对非负数值，补零右移和带符号右移产生相同结果
 *
 */

/**
 * 逻辑运算符
 * 逻辑运算符常用于布尔（逻辑）值之间; 当操作数都是布尔值时，返回值也是布尔值
 * 不过实际上&&和||返回的是一个特定的操作数的值，所以当它用于非布尔值的时候，返回值就可能是非布尔值
 *
 * 运算符	        范例:	                                        描述
 * 逻辑与 (&&)	expr1 && expr2	(逻辑与)           如果 expr1 能被转换为 false，那么返回 expr1；
 *                                                否则，返回expr2。因此，&&用于布尔值时，当操作数都为 true 时返回 true；否则返回 false.
 * 逻辑或 (||)	expr1 || expr2	(逻辑或)           如果 expr1 能被转换为 true，那么返回 expr1；
 *                                                否则，返回expr2。因此 || 用于布尔值时，当任何一个操作数为 true 则返回 true；如果操作数都是 false 则返回 false
 * 逻辑非 (!)	!expr	                          (逻辑非) 如果操作数能够转换为 true 则返回 false；否则返回 true
 */

/**
 * 短路求值
 * 作为逻辑表达式进行求值是从左到右，它们是为可能的“短路”的出现而使用以下规则进行测试：
 * false && anything // 被短路求值为 false
 * true || anything // 被短路求值为 true
 *
 * 逻辑的规则，保证这些评估是总是正确的
 * 请注意，上述表达式的anything部分不会被求值，所以这样做不会产生任何副作用
 */

/**
 * 字符串运算符
 * 除了比较操作符，它可以在字符串值中使用，连接操作符（+）连接两个字符串值相连接，
 * 返回另一个字符串，它是两个操作数串的结合
 * 例如:
 * console.log("my " + "string"); // console logs the string "my string".
 *
 * 简写操作符 += 也可以用来拼接字符串，例如：
 * var myString = "alpha";
 *
 * myString += "bet"; // 返回 "alphabet"
 */

/**
 * 逗号操作符
 * 逗号操作符（,）对两个操作数进行求值并返回最终操作数的值。它常常用在 for 循环中，在每次循环时对多个变量进行更新
 *
 * 假如 a 是一个二维数组，每个维度各有 10 个元素，以下代码利用逗号操作符来同时改变两个变量的值
 * 这段代码的功能是打印出该二维数组的对角线元素的值：
 * var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
 * var a = [x, x, x, x, x];
 *
 * for (var i = 0, j = 9; i <= j; i++, j--)
 *   console.log("a[" + i + "][" + j + "]= " + a[i][j]);
 */

/**
 * 一元操作符仅对应一个操作数
 *
 * delete
 * delete操作符，删除一个对象的属性或者一个数组中某一个键值
 * 语法如下：
 * delete objectName.property;
 * delete objectName[index];
 * delete property; // legal only within a with statement
 * objectName是一个对象名，property 是一个已经存在的属性，index是数组中的一个已经存在的键值的索引值
 *
 * 第三行的形式只在with声明的状态下是合法的，从对象中删除一个属性
 * 你能使用 delete 删除各种各样的隐式声明，但是被var声明的除外
 *
 * 如果 delete 操作成功，属性或者元素会变成 undefined
 * 如果 delete可行会返回true，如果不成功返回false
 * 示例：
 * x = 42;
 * var y = 43;
 * myobj = new Number();
 * myobj.h = 4; // create property h
 * delete x; // returns true (can delete if declared implicitly)
 * delete y; // returns false (cannot delete if declared with var)
 * delete Math.PI; // returns false (cannot delete predefined properties)
 * delete myobj.h; // returns true (can delete user-defined properties)
 * delete myobj; // returns true (can delete if declared implicitly)
 */

/**
 * 删除数组元素
 * 删除数组中的元素时，数组的长度是不变的，例如删除 a[3], a[4]，a[4] 和 a[3] 仍然存在变成了 undefined
 *
 * delete 删除数组中的一个元素，这个元素就不在数组中了
 * 例如trees[3]被删除，trees[3] 仍然可寻址并返回 undefined
 *示例：
 * var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
 * delete trees[3];
 * if (3 in trees) {
 *   // 不会被执行
 * }
 */

//【表达式】
/**
 * 表达式是一组代码的集合，它返回一个值
 *
 * 每一个合法的表达式都能计算成某个值，但从概念上讲，
 * 有两种类型的表达式：有副作用的（比如赋值）和单纯计算求值的。
 *
 * 表达式 x=7 是第一类型的一个例子
 * 该表达式使用=运算符将值 7 赋予变量 x。这个表达式自己的值等于 7
 *
 * 代码 3 + 4 是第二个表达式类型的一个例子
 * 该表达式使用 + 运算符把 3 和 4 加到一起但并没有把结果（7）赋值给一个变量
 */